docs(protocol): give INVALID_FIELD one published meaning — the name/resolution one its 22 producers send (#15800) - #15883
Merged
Conversation
`error-handling.mdx` published «Field value has wrong type» with the example message `Field 'age' must be a number`, while `error-catalog.mdx` publishes the name/resolution meaning. A repo-wide producer sweep finds 22 assignment sites in 5 files, all name/resolution-shaped, and none of the 14 type-shaped refusal texts carries `INVALID_FIELD` — so the type meaning had no emitter and the example message quoted no producer. Rewrite only the `#### INVALID_FIELD` block: the Meaning now matches the catalog, and the example message is the real producer text from `packages/rest/src/error-response.ts:1655` (`Unknown field '<field>' on object '<object>'`) with the `field`/`object` details that producer actually sets. Status stays 400, as every producer sets and `error-response.ts` maps. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
This was referenced Sep 5, 2026
baozhoutao
marked this pull request as ready for review
September 5, 2026 12:47
baozhoutao
enabled auto-merge
September 5, 2026 12:47
Contributor
Author
|
Flipped ready + auto-merge enabled (12:47Z, Generated by Claude Code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #15800
INVALID_FIELDwas published with two different meanings.content/docs/api/error-catalog.mdx:71publishes a name/resolution cause;content/docs/protocol/kernel/error-handling.mdx:302-304published «Field value has wrong type», with an example message no producer emits. This PR rewrites that one block so the wire code has one published meaning. ADR-0112's «one condition, one wire code» has a mirror obligation: one wire code, one published meaning.⛔ Scope is exactly the
#### INVALID_FIELDblock oferror-handling.mdx—error-catalog.mdxis untouched, no other block of the page is touched (the page as a whole is the subject of decision #15632), and no code changes.Measurement — re-run before editing, with a firing control
Sweep 1,
INVALID_FIELDassignment sites (non-test), at merge basee75a9040b:Triage read 21 in 4 files; the 22nd is
summary-backfill.ts:297, the roll-up producer PR #15708 was adding at filing time. It is name-shaped like the rest ("names N roll-up(s) this run cannot find").Every one of the 22 message texts was read. 22/22 are name/resolution-shaped — no site refuses a record field's value for having the wrong type, and none renders anything resembling
Field 'age' must be a number. Two of the 22 (protocol.ts:9610,:9624) refuse the shape of thesearchFieldsparameter value — still a list-of-field-names condition, not a record value's type; that nuance is not covered by the catalog's Cause clause and is filed separately as #15882, ⛔ not fixed here.Sweep 2, type-shaped refusal text (non-test):
None of the 14 carries
INVALID_FIELD(checked ±25 lines around each hit; zeroINVALID_FIELDoccurrences in every window). The only hit that is both type-shaped and carries a wire code ispackages/plugins/plugin-auth/src/auth-plugin.ts:2225, and it usesINVALID_REQUEST. The nearest thing to the deleted example —packages/spec/src/system/validation-message.ts:96,invalid_number: '{{label}} must be a number'— reachesApiError.details.fields[].codeand nevererror.code, aspackages/runtime/src/dispatcher-error-vocabulary.ts:970-980records for that exact key (ADR-0114 D2 / ADR-0112 D6). So the platform does have a "value has the wrong type" condition; it does not answer it withINVALID_FIELD.Firing control (a grep that MUST hit, so the zero above is a reading and not a broken pattern):
⇒
premise_still_valid: true. The card's sharper reading holds: «Field value has wrong type» was a meaning without an emitter.HTTP status — unchanged at 400, as the producers send it
Every one of the 22 sites sets
status = 400/status: 400. The REST mapping inpackages/rest/src/error-response.ts:1149reads:Before
After
The example message is quoted from a real producer:
packages/rest/src/error-response.ts:1655buildsUnknown field 'FIELD' on object 'OBJECT'(the object clause is appended when the object is known), rendered here with concrete values, andpackages/objectql/src/engine.ts:1252emits the identical sentence. Thedetailskeys are narrowed to the two the producer actually sets (field,object, pererror-response.ts:1149); the inventedexpected_type/actual_type/valuekeys are gone. The block keeps its shape: status line, meaning, example JSON.Verification
node scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack→ 39 commands derived. All 39 run; reconciliation with--ran: "Run reconciliation — 39 derived, 39 run, 0 NOT-MEASURED, 0 UNRUN."pnpm check:doc-anchors→ exit 0 — "✅ check-doc-anchors: 311 internal #fragment link(s) across 410 source file(s) all resolve to a real heading" (this covers the new#invalid_fieldlink).pnpm check:error-status-conformance→ exit 0 — "✓ every derivable runtime status is documented, and every documented status is reachable." This gate readscontent/docs/protocol/kernel/error-handling.mdxby name (scripts/check-error-status-conformance.mjs:219), so it is the gate that grades this page's status line.pnpm check:nul-bytes→ exit 0; plus a direct control-byte scan of the edited file,grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'→ no hits.pnpm --filter @objectstack/spec exec vitest run src/api/error-catalog-docs.test.ts→ exit 0, 3 passed. (It reads onlyerror-catalog.mdx, which this PR does not touch.)turbo run buildfor the@objectstack/lint,@objectstack/formulaand@objectstack/client-reactclosures:check:doc-formula-expressions,check:doc-security-posture,check:docs-transcript-drift,spec check:docs,spec check:skill-examples— all exit 0 afterwards.dispatch-gatesdeclares as taking a value from the workflow —check-cross-package-test-inputs --union-into "$RUNNER_TEMP/…",check-shard-attestation --shard …,check-test-completeness "$RUNNER_TEMP/test-core.log". Their argv exists only inside a CI run.node scripts/pm/check-governed-merges.mjs --test content/docs/protocol/kernel/error-handling.mdx→ exit 0, "✅ NOT governed — ordinary queue landing applies to a PR with exactly this file list."git clone --bare --sharedof the worktree,git --git-dir=probe.git config --get merge.os-regen.driver→ empty (exit 1, no driver registered), thengit merge-tree --write-tree ef3a1388d d56fb5dec→ exit 0, tree4769460269e32cffd083524eb3958a7df728e6cc. Clean againstorigin/mainas ofef3a1388d.GET /repos/objectstack-ai/objectstack/pulls/N/files. Zero open PR touchescontent/docs/protocol/kernel/error-handling.mdx. PR feat(objectql,cli):backfillSummaryNullsacceptsrecomputeUndefinedOnEmpty— a just-declared min/max/avg roll-up can be filled on request (#15064) #15708 has landed (it is no longer open; its producer is the 22nd site above) and its docs half waserror-catalog.mdx, not this page. Three open PRs touch neighbouring error docs and none touches this file or the catalog'sINVALID_FIELDentry: docs(api): the 409 unique-constraint entry is UNIQUE_VIOLATION on the wire, not DUPLICATE_RECORD #15750 and feat(spec)!: the twelve api/ duration keys carry their unit in the key name (#15677, stack 2/6) #15837 (error-catalog.mdx), fix(runtime): refusePOST /packages/:id/duplicateon a source that is not a base #15849 (error-code-ledger.mdx).Docs-only, no user-visible package change ⇒
skip-changeset.🤖 Generated with Claude Code
https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
Generated by Claude Code